using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Crusades.Helpers;
using DW.IntelligenceSource.GUI.Comparers;

namespace DW.IntelligenceSource.GUI.Map {
  public class MapTransform {
    public virtual void applyZoom(UIElement canvas, Rect viewPort, double zoomFactor) {
      //todo:Use of the GetOffset method could be a source of the bugs we see in zooming the map
      Vector canvasOffset = VisualTreeHelper.GetOffset(canvas);
      Point viewPointCenter = new Point{
        X = (viewPort.X - canvasOffset.X + viewPort.Width/2),
        Y = (viewPort.Y - canvasOffset.Y + viewPort.Height/2)
      };
      var transform = new ScaleTransform(zoomFactor, zoomFactor, viewPointCenter.X, viewPointCenter.Y);
      addToCurrentTransform(canvas, transform);
    }

    //You can only pan if as a result of the pan the map is entirely outside the viewport.
    public virtual TranslateTransform applyPan(Canvas canvas, Rect viewPort, Vector newVector) {
      var T = new TranslateTransform(newVector.X, newVector.Y);
      if (transformWouldMoveMapInsideViewport(canvas, viewPort, T)) {
        return null;
      }
      addToCurrentTransform(canvas, T);
      return T;
    }

    public virtual Transform AddIdentityTransform(Canvas canvas, Type typeOfTransform){
      Transform t;
      if(typeOfTransform == typeof(TranslateTransform)){
        t = new TranslateTransform(0, 0);
      }
      else{
        t = new ScaleTransform(1, 1);
      }
      addToCurrentTransform(canvas,t);
      return t;
    }

    public virtual bool IsOutsideViewport(Rect viewport, Rect map) {
      bool returnValue = map.Left < viewport.Left &&
                         map.Right > viewport.Right &&
                         map.Top < viewport.Top &&
                         map.Bottom > viewport.Bottom;
      return returnValue;
    }

    public virtual bool transformWouldMoveMapInsideViewport(Canvas mapCanvas, Rect viewPort, TranslateTransform T) {
      Rect currentCanvas = mapCanvas.GetRect();
      TransformGroup resultingTransform =
          addTransformToExistingGroup(mapCanvas, T);
      Rect transformedCanvas = GetTransformedRectangle(mapCanvas, viewPort, resultingTransform, currentCanvas);
      return !IsOutsideViewport(viewPort, transformedCanvas);
    }

    private Rect GetTransformedRectangle(Canvas mapCanvas, Rect viewPort, TransformGroup resultingTransform, Rect currentCanvas) {
      Rect transformedCanvas;
      if (mapCanvas.ActualWidth >= viewPort.Width) {
        transformedCanvas = resultingTransform.TransformBounds(currentCanvas);
      } else {
        transformedCanvas = resultingTransform.TransformBounds(new Rect(resultingTransform.Transform(new Point(0, 0)),
                                    resultingTransform.Transform(new Point(currentCanvas.Width, currentCanvas.Height))));
      }
      return transformedCanvas;
    }

    private void addToCurrentTransform(UIElement canvas, Transform T) {
      TransformGroup group = addTransformToExistingGroup(canvas, T);
      canvas.RenderTransform = group;
    }

    private TransformGroup addTransformToExistingGroup(UIElement canvas, Transform T) {
      Transform oldTransform = canvas.RenderTransform;
      var group = new TransformGroup();
      group.Children.Add(oldTransform);
      group.Children.Add(T);
      return group;
    }
  }
}